// openaiTtsService.js - Updated with support for voice instructions

import { getAudioDuration } from '../util/ffmpeg'
import { execFile } from 'child_process'
import util from 'util'
import fs from 'fs'
import path from 'path'
import { app } from 'electron'
import OpenAI from 'openai'
import { getRandomEnabledApiKey } from '../dao/apiKeys.js'
import { getProxyConfig } from '../dao/proxyConfig.js'

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

// 获取应用数据目录，用于保存合成的音频文件
const getSynthesizedAudioDir = () => {
  const userDataPath = app.getPath('userData');
  const audioDir = path.join(userDataPath, 'synthesized_audio');

  // 确保目录存在
  if (!fs.existsSync(audioDir)) {
    fs.mkdirSync(audioDir, { recursive: true });
  }

  return audioDir;
};

/**
 * 将文本转换为语音
 * @param {string} text - 要转换的文本
 * @param {string} model - 使用的模型: 'tts-1', 'tts-1-hd', 'gpt-4o-audio-preview', 'gpt-4o-mini-tts'
 * @param {string} voice - 使用的语音，如 'alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer'
 * @param {number} language - 文本语言，可以在提示中使用
 * @param {string} instructions - 可选的语音特性指令，用于 gpt-4o-mini-tts 模型
 * @returns {Promise<object>} - 返回包含路径和时长的对象
 */
export const textToSpeech = async (text, model = 'tts-1', voice = 'alloy', language = null, instructions = null) => {

  // 从数据库获取随机启用的API密钥
  const apiKeyData = getRandomEnabledApiKey();

  if (!apiKeyData) {
    throw new Error('没有启用的API密钥，请在API密钥管理页面添加并启用至少一个密钥');
  }

  // 获取代理配置
  const proxyConfig = getProxyConfig();

  try {
    console.log('初始化OpenAI客户端');
    console.log(`使用API密钥: ${apiKeyData.name}`);
    console.log(`使用模型: ${model}, 语音: ${voice}`);

    if (instructions) {
      console.log(`使用语音指令: ${instructions}`);
    }

    // 根据代理配置创建OpenAI客户端
    const openaiConfig = {
      apiKey: apiKeyData.key,
      baseURL: proxyConfig.proxy_url
    };

    const openai = new OpenAI(openaiConfig);

    let filePath;
    let duration;

    // 处理新的 gpt-4o-mini-tts 模型
    if (model === 'gpt-4o-mini-tts') {
      console.log('发送gpt-4o-mini-tts音频生成请求:', {
        model,
        voice,
        instructionsLength: instructions ? instructions.length : 0,
        textLength: text.length
      });

      // 使用 gpt-4o-mini-tts API
      const response = await openai.audio.speech.create({
        model: model,
        voice: voice,
        input: text,
        instructions: instructions || undefined  // 如果有语音指令则传递
      });

      // 生成唯一文件名
      const timestamp = Date.now();
      const fileName = `speech_${timestamp}.mp3`;
      filePath = path.join(getSynthesizedAudioDir(), fileName);

      console.log('保存gpt-4o-mini-tts音频文件:', filePath);

      // 将arrayBuffer转换为Buffer并写入文件
      const buffer = Buffer.from(await response.arrayBuffer());
      await fs.promises.writeFile(filePath, buffer);

      // 估算音频时长
      duration = await getAudioDuration(filePath)
    }
    // 处理GPT-4o音频预览模型
    else if (model === 'gpt-4o-audio-preview' || model === 'gpt-4o-mini-audio-preview') {
      // 构建消息内容
      let prompt = text;
      if (language) {
        prompt = `Please speak this text in ${language}: ${text}`;
      }

      // 如果提供了指令，添加到提示中
      if (instructions) {
        prompt = `${instructions}\n\n${prompt}`;
      }

      console.log('发送GPT-4o音频生成请求:', {
        model,
        voice,
        language,
        text: text,
        textLength: text.length
      });

      // 使用GPT-4o音频生成API
      const response = await openai.chat.completions.create({
        model: model,
        modalities: ["text", "audio"],
        audio: { voice: voice, format: "mp3" },
        messages: [
          {
            role: "user",
            content: prompt
          }
        ],
        store: true,
      });

      // 从响应中提取音频数据
      const audioData = response.choices[0].message.audio.data;

      // 生成唯一文件名
      const timestamp = Date.now();
      const randomNum = Math.floor(1000 + Math.random() * 9000);
      const fileName = `speech_${timestamp}_${randomNum}.mp3`;
      filePath = path.join(getSynthesizedAudioDir(), fileName);

      console.log('保存GPT-4o音频文件:', filePath);

      // 将base64编码的音频数据写入文件
      fs.writeFileSync(
        filePath,
        Buffer.from(audioData, 'base64')
      );

      // 估算音频时长
      duration = await getAudioDuration(filePath)
    }
    // 处理传统TTS模型
    else {
      console.log('发送TTS音频生成请求:', {
        model,
        voice,
        textLength: text.length
      });

      // 使用TTS API直接生成语音
      const mp3 = await openai.audio.speech.create({
        model: model,
        voice: voice,
        input: text,
      });

      // 生成唯一文件名
      const timestamp = Date.now();
      const fileName = `speech_${timestamp}.mp3`;
      filePath = path.join(getSynthesizedAudioDir(), fileName);

      console.log('保存TTS音频文件:', filePath);

      // 将arrayBuffer转换为Buffer并写入文件
      const buffer = Buffer.from(await mp3.arrayBuffer());
      await fs.promises.writeFile(filePath, buffer);

      // 估算音频时长
      duration = await getAudioDuration(filePath)
    }

    return {
      path: filePath,
      duration: duration // 返回估计的音频时长（秒）
    };
  } catch (error) {
    console.error('OpenAI音频生成失败:', error);

    // 提取并记录错误详细信息
    if (error.response) {
      console.error('错误状态:', error.response.status);
      console.error('错误详情:', error.response.data);
    }

    throw new Error(`音频生成失败: ${error.message}`);
  }
};

/**
 * 使用OpenAI进行文本翻译
 * @param {string} text - 要翻译的文本
 * @param {string} sourceLang - 源语言（可选）
 * @param {string} targetLang - 目标语言
 * @returns {Promise<string>} - 返回翻译后的文本
 */
export const translateText = async (text, sourceLang = 'auto', targetLang = 'zh-CN') => {
  console.log("执行OpenAI翻译文本");

  // 从数据库获取随机启用的API密钥
  const apiKeyData = getRandomEnabledApiKey();

  if (!apiKeyData) {
    throw new Error('没有启用的API密钥，请在API密钥管理页面添加并启用至少一个密钥');
  }

  // 获取代理配置
  const proxyConfig = getProxyConfig();

  try {
    console.log('初始化OpenAI客户端');
    console.log(`使用API密钥: ${apiKeyData.name}`);

    // 根据代理配置创建OpenAI客户端
    const openaiConfig = {
      apiKey: apiKeyData.key,
      baseURL: proxyConfig.proxy_url
    };

    const openai = new OpenAI(openaiConfig);

    // 构建翻译提示
    const prompt = `请将以下文本翻译成${targetLang}，只输出翻译结果：\n\n${text}`;

    // console.log(prompt)

    console.log('发送翻译请求:', {
      textLength: text.length
    });

    // 使用OpenAI API进行翻译
    const response = await openai.chat.completions.create({
      model: 'gpt-4o-mini',  // 使用高质量翻译模型
      messages: [
        {
          role: 'system',
          content: `## 角色&任务

### 任务

我希望你以一个专业翻译团队的身份，协助完成从${sourceLang === 'auto' ? '自动识别的源语言' : sourceLang}到${targetLang}的翻译任务。最终必须输出${targetLang}语言的翻译结果。

### 角色

对于每个翻译任务，我将扮演三个专家角色，分别负责翻译、校对与润色工作:

1. 翻译专家:具有20年翻译经验,精通源语言和${targetLang}双语，并拥有丰富的跨学科知识。此阶段的目标是提供一份既忠实于原文，又在${targetLang}中读起来流畅自然的初稿。在翻译时，特别注重保持原文的风格和语调。

2.资深校对编辑:拥有20年专业编辑经验，${targetLang}系毕业，对${targetLang}语法、用词有精准把握。在此阶段，您需要对翻译稿进行深度校对，包括语法、用词、风格的校正，确保翻译的准确性和易读性,进而输出第二版${targetLang}翻译稿。

3.润色专家:你是个写了20年文章的获奖作家，什么风格的${targetLang}文章都能写。这一步你要拿校对编辑改好的稿子，再润色一下，让文字更有美感，但还要保持原文的专业性和准确性。特别是针对足球、娱乐、运动、电竞、新闻以及解说类型的内容，要用更生动、更有激情的${targetLang}表达，让读者感受到现场的氛围和情绪。

## 工作流程

### 1.翻译阶段

参与人:翻译专家

输出物:${targetLang}翻译稿件

任务:提供忠实原文且流畅的${targetLang}初稿。

### 2.校对阶段

参与人:资深校对编辑
输出物:校对过的${targetLang}翻译稿件

任务:深度校对初稿，保证准确性和易读性。

### 3.润色阶段

参与人:润色专家

输出物:润色过后的最终${targetLang}翻译稿

任务:让文字更好看更有味道，但要保持专业和准确。

## 重要提醒
请直接输出最终的${targetLang}翻译结果，不要包含任何中间过程或解释说明。如有时间轴，请保留时间轴原格式。

## 特别注意事项
1. **人名翻译要统一和规范**：
   - 中文人名要按照正确的音译规范转换，避免音义不符
   - 同一个人的名字在全文中要保持一致

2. **保持原文的语言风格**：
   - 如果原文是口语化的，翻译也要口语化
   - 网络用语、俚语要找到对应的${targetLang}表达
   - 保持原文的情感色彩和语气

3. **让句子读起来自然流畅**：
   - 不要生硬直译，要符合${targetLang}的表达习惯
   - 逻辑连接要自然，避免翻译腔
   - 关键细节要准确还原，不能丢失原意`
        },
        {
          role: 'user',
          content: prompt
        }
      ],
      temperature: 0.3,  // 较低的温度确保更稳定的翻译
    });

    // 提取翻译结果
    const translatedText = response.choices[0].message.content.trim();

    console.log('翻译完成，原文长度:', text.length, '翻译后长度:', translatedText.length);

    return translatedText;
  } catch (error) {
    console.error('OpenAI翻译失败:', error);

    // 提取并记录错误详细信息
    if (error.response) {
      console.error('错误状态:', error.response.status);
      console.error('错误详情:', error.response.data);
    }

    throw new Error(`翻译失败: ${error.message}`);
  }
};

/**
 * 使用OpenAI进行文本翻译
 * @param {string} text - 要翻译的文本
 * @param {string} sourceLang - 源语言（可选）
 * @param {string} targetLang - 目标语言
 * @returns {Promise<string>} - 返回翻译后的文本
 */
export const translateSubtitles = async (text, sourceLang = 'auto', targetLang = 'zh-CN') => {
  console.log("执行OpenAI翻译字幕");

  // 从数据库获取随机启用的API密钥
  const apiKeyData = getRandomEnabledApiKey();

  if (!apiKeyData) {
    throw new Error('没有启用的API密钥，请在API密钥管理页面添加并启用至少一个密钥');
  }

  // 获取代理配置
  const proxyConfig = getProxyConfig();

  try {
    console.log('初始化OpenAI客户端');
    console.log(`使用API密钥: ${apiKeyData.name}`);

    // 根据代理配置创建OpenAI客户端
    const openaiConfig = {
      apiKey: apiKeyData.key,
      baseURL: proxyConfig.proxy_url
    };

    const openai = new OpenAI(openaiConfig);

    // 构建翻译提示
    const prompt = `请将以下文本翻译成${targetLang}，只输出翻译结果：\n\n${text}`;

    // console.log(prompt)

    console.log('发送翻译请求:', {
      textLength: text.length
    });

    // 使用OpenAI API进行翻译
    const response = await openai.chat.completions.create({
      model: 'gpt-4o-mini',  // 使用高质量翻译模型
      messages: [
        {
          role: 'system',
          content: `## 角色&任务

### 任务

我希望你以一个**专业翻译团队**的身份，协助完成从${sourceLang === 'auto' ? '自动识别的源语言' : sourceLang}到${targetLang}的翻译任务。最终必须输出**仅包含${targetLang}的最终翻译结果**。

### 角色

对于每个翻译任务，我将扮演三个专家角色，分别负责翻译、校对与润色工作：

1. **翻译专家**
   - 具有20年翻译经验，精通源语言和${targetLang}双语，并拥有丰富的跨学科知识。
   - 目标：提供忠实原文、自然流畅的${targetLang}初稿。
   - 要求：特别关注口语化、感叹、重复、拟声等情绪表达，确保译文保留原文的语气和节奏。
2. **资深校对编辑**
   - 拥有20年专业编辑经验，${targetLang}系毕业，对${targetLang}语法、用词有精准把握。
   - 目标：逐句深度校对翻译初稿，输出准确、易读、自然的${targetLang}文本。
   - 要求：
     - 保证语法正确、用词得体。
     - 在游戏、足球、娱乐、解说等语境中，使用符合${targetLang}玩家或观众习惯的术语与表达（如“送人头”应为“feed mạng / tặng mạng”）。
     - 避免直译或书面化，保持口语感。
3. **润色专家**
   - 你是一位写作20年的获奖作家，擅长多种文风。
   - 目标：在校对稿基础上润色，使译文更有表现力与感染力。
   - 要求：
     - 语言更生动、更有激情，贴近玩家/观众的真实口语。
     - 保持原文的节奏感与情绪张力（如“你看你看你看你看”→“Nhìn kìa nhìn kìa nhìn kìa!”）。
     - 在保证专业与准确的前提下，让字幕更像“越南玩家/解说在现场说的话”。

## 工作流程

### 1. 翻译阶段

- **参与人**: 翻译专家
- **输出物**: ${targetLang}翻译稿件
- **任务**: 提供忠实原文且流畅自然的${targetLang}初稿。

### 2. 校对阶段

- **参与人**: 资深校对编辑
- **输出物**: 校对过的${targetLang}翻译稿件
- **任务**: 深度校对初稿，确保准确性、口语化和专业术语的正确使用。

### 3. 润色阶段

- **参与人**: 润色专家
- **输出物**: 润色过后的最终${targetLang}翻译稿
- **任务**: 让字幕更生动自然，保持节奏感和情绪感染力，同时确保专业和准确。

## 重要提醒

- **最终只输出润色后的${targetLang}翻译结果**，不要包含任何中间过程或解释说明。
- 如果原文包含时间轴（如字幕文件），请保留时间轴格式不变。

## 特别注意事项

1. **人名翻译要统一和规范**
   - 中文人名应按照正确音译规范转换，避免音义不符。
   - 同一人名在全文保持一致。
2. **保持原文的语言风格和情绪色彩**
   - 原文口语化，译文也必须口语化。
   - 网络用语、俚语、游戏/解说常用语要对应到${targetLang}中玩家/观众常说的表达。
   - 保留原文的情绪和急促感，尤其是连续抱怨、重复句。
3. **让句子读起来自然流畅**
   - 避免生硬直译，要符合${targetLang}的表达习惯。
   - 保证逻辑自然，避免翻译腔。
   - 不丢失细节，还原原意。
   - 拟声、重复词必须保留节奏和语气（如“no no no no no” → “Không không không không!”）
`
        },
        {
          role: 'user',
          content: prompt
        }
      ],
      temperature: 0.3,  // 较低的温度确保更稳定的翻译
    });

    // 提取翻译结果
    const translatedText = response.choices[0].message.content.trim();

    console.log('翻译完成，原文长度:', text.length, '翻译后长度:', translatedText.length);

    return {
      ok: true,
      data: translatedText
    };
  } catch (error) {
    console.error('OpenAI翻译失败:', error);

    // 提取并记录错误详细信息
    if (error.response) {
      console.error('错误状态:', error.response.status);
      console.error('错误详情:', error.response.data);
    }

    throw new Error(`翻译失败: ${error.message}`);
  }
};

/**
 * 批量翻译多个文本项
 * @param {Array<Object>} items - 要翻译的项目列表，每项包含id, text等字段
 * @param {string} targetLang - 目标语言
 * @returns {Promise<Array<Object>>} - 返回翻译后的项目列表
 */
export const batchTranslate = async (items, targetLang = 'zh-CN') => {
  console.log(`批量翻译 ${items.length} 个项目到 ${targetLang}`);

  const results = [];

  for (const item of items) {
    try {
      console.log(`翻译项目 ID: ${item.id}, 名称: ${item.name}`);

      // 翻译标题和内容
      const [translatedName, translatedText] = await Promise.all([
        item.name ? translateText(item.name, 'auto', targetLang) : Promise.resolve(item.name),
        translateText(item.text, 'auto', targetLang)
      ]);

      results.push({
        ...item,
        name: translatedName || item.name,
        text: translatedText
      });

    } catch (error) {
      console.error(`翻译项目 ID: ${item.id} 失败:`, error);
      // 翻译失败时保留原始文本
      results.push(item);
    }
  }

  return results;
};

